﻿#region Copyright

//  ======================================================
//      Copyright (c) 2011 Toulr All rights reserved.
//     
//      The use and distribution terms for this software are contained in the file
//      named license.txt, which can be found in the root of this distribution.
//      By using this software in any fashion, you are agreeing to be bound by the
//      terms of this license.
//     
//     You must not remove this notice, or any other, from this software.
//  ======================================================

#endregion

#region Using Namespaces

using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;

#endregion

namespace Toulr.Test.Util
{
    /// <summary>
    ///   用于Linq学习的测试类
    /// </summary>
    [TestFixture]
    public class LinqFixture
    {
        [Test]
        public void StandardQuery()
        {
            /*
             *  查询是一种从数据源检索数据的表达式。 查询通常用专门的查询语言来表示。 随着时间的推移，人们已经为各种数据源开发了不同的语言；
             *  例如，用于关系数据库的 SQL 和用于 XML 的 XQuery。 因此，开发人员不得不针对他们必须支持的每种数据源或数据格式而学习新的查询语言。 
             *  LINQ 通过提供一种跨各种数据源和数据格式使用数据的一致模型，简化了这一情况。 在 LINQ 查询中，始终会用到对象。 可以使用相同的基本
             *  编码模式来查询和转换 XML 文档、SQL 数据库、ADO.NET 数据集、.NET 集合中的数据以及对其有 LINQ 提供程序可用的任何其他格式的数据。 
             *  所有 LINQ 查询操作都由以下三个不同的操作组成： 获取数据源、创建查询、 执行查询
             */
            // 下面演示了一个LINQ查询的三个最基本步骤
            //  1. 确定要查询的数据源
            var numbers = new [] {0, 1, 2, 3, 4, 5, 6};
            // 2. 创建查询表达式 表达式返回的numQuery是一个IEnumerable<int>类型的对象
            var numQuery =
                from num in numbers
                where (num%2) == 0
                select num;
            // 3. 执行查询
            foreach (int num in numQuery)
            {
                Console.Write("{0,1} ", num);
            }
            //值得注意的是，在 LINQ 中，查询的执行与查询本身截然不同；换句话说，如果只是创建查询变量，则不会检索任何数据。由于查询变量本身从不保存查询结果，
            //因此可以根据需要随意执行查询。 例如，可以通过一个单独的应用程序持续更新数据库。 在应用程序中，可以创建一个检索最新数据的查询，并可以按某一
            //时间间隔反复执行该查询以便每次检索不同的结果。 如果有需要可以通过调用一些聚合方法强制查询立即执行，此外，还可以通过在紧跟查询表达式之后的位置
            //放置一个 foreach 循环来强制执行查询。 但是，通过调用 ToList 或 ToArray，也可以将所有数据缓存在单个集合对象中。 
            //下面语句是LINQ的另一种写法（基于方法的查询），其效果与上文的查询语句相同
            numbers = new[] { 10, 11, 12, 13, 14, 15, 16 };
            IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);
            var count = numQuery2.Count();
            Console.WriteLine("\n{0,1}个记录符合条件。", count);
            /*
             * 若要了解基于方法的查询，让我们进一步地分析它。 注意，在表达式的右侧，where 子句现在表示为对 numbers 对象的实例方法，
             * 在您重新调用该对象时其类型为 IEnumerable<int>。 如果您熟悉泛型 IEnumerable<T> 接口，那么您就会了解，它不具有 Where 方法。 
             * 但是，如果您在 Visual Studio IDE 中调用 IntelliSense 完成列表，那么您不仅将看到 Where 方法，而且还会看到许多其他方法，如 Select、SelectMany、Join 和 Orderby。 
            * 在上面的示例中，条件表达式 (num % 2 == 0) 是作为内联参数传递到 Where 方法的：Where(num => num % 2 == 0). 。此内联表达式称为 lambda 表达式。 
            * 将代码编写为匿名方法或泛型委托或表达式树是一种便捷的方法，否则编写起来就要麻烦得多。 在 C# 中，=> 是 lambda 运算符，可读为“goes to”。
            * 运算符左侧的 num 是输入变量，与查询表达式中的 num 相对应。 编译器可推断 num 的类型，因为它了解 numbers 是泛型 IEnumerable<T> 类型。
            * lambda 表达式与查询语法中的表达式或任何其他 C# 表达式或语句中的表达式相同；它可以包括方法调用和其他复杂逻辑。 “返回值”就是表达式结果。
            * 若要开始使用 LINQ，您不必大量使用 lambda。 但是，特定查询只可使用方法语法表示，其中一些查询需要使用 lambda 表达式。  
            */
            foreach (int num in numQuery2)
            {
                Console.Write("{0,1} ", num);
            }
            Console.WriteLine("\n+++++++++++++++++++++++++++++++++++++++++=");
            //查询对象
           IList<User> users =new List<User>()
                                  {
                                      new User{Age = 11,UserEmail = "a@ss.com",UserName = "a"},
                                      new User{Age = 21,UserEmail = "b@ss.com",UserName = "b"},
                                      new User{Age = 21,UserEmail = "c@ss.com",UserName = "c"},
                                      new User{Age = 32,UserEmail = "d@ss.com",UserName = "d"},
                                      new User{Age = 33,UserEmail = "e@ss.com",UserName = "e"},
                                      new User{Age = 15,UserEmail = "f@ss.com",UserName = "f"}
                                  };
            //编写查询表达式
            var userQueryResult = users.Where(a => a.Age > 10 && a.Age < 30).OrderByDescending(a => a.UserName) ;
            //输出结果
            foreach (var user in userQueryResult)
            {
                Console.WriteLine(user.UserName);
            }
            //更改数据源内容
            users = new List<User>()
                                  {
                                      new User{Age = 11,UserEmail = "a1@ss.com",UserName = "a1"},
                                      new User{Age = 21,UserEmail = "b1@ss.com",UserName = "b1"},
                                      new User{Age = 21,UserEmail = "c1@ss.com",UserName = "c1"},
                                      new User{Age = 32,UserEmail = "d1@ss.com",UserName = "d1"},
                                      new User{Age = 33,UserEmail = "e1@ss.com",UserName = "e1"},
                                      new User{Age = 15,UserEmail = "f1@ss.com",UserName = "1f"}
                                  };
            //更改数据源内容后，发现输出没有改变
            foreach (var user in userQueryResult)
            {
                Console.WriteLine(user.UserName);
            }
            //
            userQueryResult = users.Where(a => a.Age > 10 && a.Age < 30).OrderByDescending(a => a.UserName);
            foreach (var user in userQueryResult)
            {
                Console.WriteLine(user.UserName);
            }
        }
    }

    public class User
    {
        public string UserName { get; set; }
        public String UserEmail { get; set; }
        public int Age { get; set; }
    }
}